<template>
  <div class="uploader-file" :status="status">
    
      <div class="uploader-file-progress" :class="progressingClass" :style="progressStyle"></div>
      <div class="uploader-file-info">
        <div class="uploader-file-name"><i class="uploader-file-icon" :icon="fileCategory"></i>{{file.fileName}}</div>
        <div class="uploader-file-size">{{formatedSize}}</div>
        <div class="uploader-file-meta"></div>
        <div class="uploader-file-status">
          <span v-show="status !== 'uploading'">{{statusText}}</span>
          
          <span v-show="status === 'uploading'">
            <span>{{progressStyle.progress}}</span>
            <em>{{formatedAverageSpeed}}</em>
            <i>{{formatedTimeRemaining}}</i>
          </span>
        </div>
       
        <div class="uploader-file-actions">

          <span class="uploader-file-retry" @click="retry"></span>
          <span class="uploader-file-remove" @click="remove"></span>
        </div>
        </div>
  </div>
    
</template>

<script>
  import { Modal,message } from 'ant-design-vue';
  import  ReadableStream  from "../common/web-streams-polyfill.min"
  import Blob from '../common/Blob'
  import streamSaver from 'streamsaver'
  import JsZip from "jszip"
  const FileSaver = require('file-saver');
  import ZipStream from '../common/zip-stream';
  import minioUploaderConfig from '../common/config'
  import { secondsToStr,formatSize,defautlCategoryMap,getExtension ,arrayBufferConcat} from '../common/utils'
  import { uploaderMixin } from '../common/mixins'

 
  const COMPONENT_NAME = 'download-file'
  
  import {mergerFile, downloadAttach, bindBusKey, deleteAttach, getAttaches,deleteChunks,downloadChunks,downFileZip } from '../api/upload'
  //

  export default {
    name: COMPONENT_NAME,
    mixins: [uploaderMixin],
    props: {
      file: {
        type: Object,
        default () {
          return {}
        }
      },
      index:{
        type:Number
      }
      
    },
    data () {
      return {
        response: null,
        paused: false,
        error: false,
        averageSpeed: 0,
        currentSpeed: 0,
        size: 0,
        formatedSize: '',
        uploadedSize: 0,
        progress: 0,
        timeRemaining: 0,
        type: '',
        extension: '',
        progressingClass: '',
        chunks:[],
        progressList:[],
        fileStream:null,
        writer:null,
        cancelHandleList:[],
        fileStatusText:{
         
          success: '下载成功',
          error: '下载出错',
          uploading: '下载中',
          waiting:'等待中'
      
        },
        startTime:0,
        isUploading:false,
        isComplete:false,
        streamCloseStatus:0,
        zipDownload:false,
        saveType:''
      }
    },
    computed: {
      
      
      fileCategory () {
        const extension = this.extension
        let type = 'unknown'
        const categoryMap = this.uploader.uploader.opts.categoryMap
        const typeMap = categoryMap || defautlCategoryMap
        Object.keys(typeMap).forEach((_type) => {
          const extensions = typeMap[_type]
          if (extensions.indexOf(extension) > -1) {
            type = _type
          }
        })
        return type
      },
      progressStyle () {
        const progress = this.progress
        const style = `translateX(${Math.floor(progress - 100)}%)`
        return {
          progress: `${progress}%`,
          webkitTransform: style,
          mozTransform: style,
          msTransform: style,
          transform: style
        }
      },
      status () {
        const isUploading = this.isUploading
        const isComplete = this.isComplete
        const isError = this.error 
       
         if  (isComplete) {
          return 'success'
        } else if (isError) {
          return 'error'
        } else if (isUploading) {
          return 'uploading'
        }  else {
          return 'waiting'
        }
      },
      formatedTimeRemaining(){
        const timeRemaining = this.timeRemaining
        const file = this.file
        if (timeRemaining === Number.POSITIVE_INFINITY || timeRemaining === 0) {
          return '0'
        }
        let parsedTimeRemaining = secondsToStr(timeRemaining)
        
        return '剩余时间'+parsedTimeRemaining
      },
      formatedAverageSpeed () {
        return `${formatSize(this.averageSpeed)} / 秒`
      },
       statusText () {
        const status = this.status
        const fileStatusText = this.fileStatusText 
        let txt = status
        if (typeof fileStatusText === 'function') {
          txt = fileStatusText(status, this.response)
        }else {
          txt = fileStatusText[status]
        }
        return txt || status
      },

    },
    watch: {
     status (newStatus, oldStatus) {
        if (oldStatus && newStatus === 'uploading' && oldStatus !== 'uploading') {
          this.tid = setTimeout(() => {
            this.progressingClass = 'uploader-file-progressing'
          }, 200)
        } else {
          clearTimeout(this.tid)
          this.progressingClass = ''
        }
      },
      index(){
        
         this.download() 
      }
    },
    methods: {
       
      handleProgress(offset,loaded){
        let totalSize = this.file.size 
         // 保存每个分片的上传进度
        this.progressList[offset] = loaded;
        // 计算已上传的总进度
        const complete = this.progressList.reduce((p, c) => p + c) || 0;
        let endTime = Math.ceil( (new Date().getTime() - this.startTime)  / 1000)
        this.averageSpeed = Math.ceil(complete/endTime)
        this.timeRemaining = Math.floor((totalSize - complete) / this.averageSpeed)
        this.progress =  Math.floor((complete / totalSize) * 100);
      },
      downloadParams(file){
       let params = {
          busKey:file.busKey,
          busField:file.busField,
          busType:file.busType,
          identifier:file.identifier,
          objectName:file.objectName,
          fileName:file.fileName,
          ftpKey:this.uploader.query.ftpKey,
          path:file.path,
          id:file.id,
          attachIds:file.id,
          size:file.size,
       } 
     params = Object.assign(params ,this.uploader.query) 
       return params;
      },
    
      bootstrapZip:async function () {
        //打包下载
        var _this = this;
        

        let fileList = _this.file.fileList  
        streamSaver.mitm = minioUploaderConfig.mitmURL
        this.fileStream = streamSaver.createWriteStream(this.fileName)
        
        const readableZipStream = new ZipStream({
          async pull (ctrl) {
             for (let index = 0; index < fileList.length; index++) {
              const element = fileList[index];
              _this.progressList.push(0)
              let params = _this.downloadParams(element)
              params.offset=index
              let blob = await  downFileZip(params,_this) 
              
              ctrl.enqueue({ name:element.fileName, stream:() =>blob.stream() })
            }
            ctrl.close()
          }
        })

        
   // more optimized
        if (window.WritableStream && readableZipStream.pipeTo) {
          return readableZipStream.pipeTo(_this.fileStream).then(() => {
             _this._fileSuccess();
             console.log('完成下载')
          })
        }
    // less optimized
        this.writer  = this.fileStream.getWriter()
        const writer = this.writer.getWriter()
        const reader = readableZipStream.getReader()
        const pump = () => reader.read()
          .then(res =>{
            if(res.done){
               _this._fileSuccess();
                writer.close()
            }else{
              writer.write(res.value).then(pump).catch(()=>{
                _this._fileError()
              })
            }
          } ).catch(()=>{
             _this._fileError()
          })

        pump()

      },
     
        bootstrap:async function () {
        var _this = this;  
       
        this.startTime = new Date().getTime()  
        this._fileDownload();
        this.progress = 0
        this.progressList=[]
        this.cancelHandleList = []

        //打包下载
        if(_this.zipDownload){
          _this.bootstrapZip()
          return
        }
       
        //分块文件下载
        var opts = this.uploader.uploader.opts
        this.chunks = []
    
        // Rebuild stack of chunks from file
        let chunkSize = opts.chunkSize 
        var round = opts.forceChunkSize ? Math.ceil : Math.floor
        var chunks = Math.max(round(this.size / chunkSize), 1)
         streamSaver.mitm = minioUploaderConfig.mitmURL
        this.fileStream = streamSaver.createWriteStream(this.fileName, {
          size : this.size
        })
        this.writer  = this.fileStream.getWriter()
        //流关闭判断
        this.streamCloseStatus = 0
        let currentOffset = 0
        let flag = true
        for (var offset = 0; offset < chunks; offset++) {
          if(!flag){return}
         
          this.progressList.push(0)
          let startByte = offset * chunkSize
          let endByte = this.computeEndByte(offset,opts,this.file)
          let currentChunkSize =  endByte - startByte
          let chunk =  {startByte,endByte}
          this.chunks.push(chunk)
         
          let params = this.uploader.processParams(null)
          params.startByte=startByte
          params.endByte=endByte
          params.offset=offset
          params.chunkNumber= offset + 1 
          params.currentChunkSize= currentChunkSize
          params.id = this.file.id
          params = Object.assign(params,this.downloadParams(this.file)) 
         
          let blob = await  downloadChunks(params,this)       
          const readableStream = blob.stream()
          const reader = readableStream.getReader()
          const pump = async  () =>{
            
            let read =  await reader.read()
           
            if(read.done){
              //读取结束
              
              currentOffset  = currentOffset + 1
              if(currentOffset == chunks ){
                
                _this._fileSuccess();
                
              }
              
            }else{
              
              //读取中
              let writeRes =   await new Promise((resolve,reject)=>{
                  _this.writer.write(read.value).then(resolve(1)).catch(reject(0))
                  
              } )
              if(writeRes== 1){
               await pump()
               
              }else{
                //出错
                
                this._fileError()
               flag = false
               
              }
            }
        }
        
         await pump()
         
        }
     },
      cancelHandle() {
        //取消下载
         this.streamClose()
        this.cancelHandleList.forEach(fn => fn());
      },
       pause () {
        
      },
      resume () {
        
      },
      remove(){
        this.cancelHandle();
        this.uploader.downloadFinish(this.file.id)
      },
      retry () {
        this.cancelHandle();
        this.bootstrap();
      },
     _fileSuccess(){
        this.streamClose()
         this.error = false
        this.isComplete = true
        this.isUploading = false
          
        this.uploader.downloadFinish(this.file.id)
     },
     isWaiting(){
       return !(this.error || this.isComplete || this.isUploading)
     },
     async streamClose(){
       let _this = this
       if(!!this.fileStream){
          if(!this.fileStream.locked){
           this.fileStream.abort()
        } else if(!!this.writer){
          
          if(this.streamCloseStatus  == 0){
           let streamCloseStatus = await new Promise((resolve,reject)=>{ _this.writer.close().then(resolve(1)).catch(reject(0))})
           this.streamCloseStatus = streamCloseStatus
          }  

          
        }
       
       }

       
     },
     _fileError(){
      this.streamClose()
       this.error = true
        this.isComplete = false
        this.isUploading = false
       
     },
     _fileDownload(){
       this.error = false
        this.isComplete = false
        this.isUploading = true
        
     },
      computeEndByte: function (offset,opts,file) {
        var endByte = Math.min(file.size, (offset + 1) * opts.chunkSize)
        if (file.size - endByte < opts.chunkSize && !opts.forceChunkSize) {
          // The last chunk will be bigger than the chunk size,
          // but less than 2 * this.chunkSize
          endByte = file.size
        }
        return endByte
      },
       getFormatSize () {
        var size = this.file.size
        this.formatedSize = formatSize(size)
      },
      getExtension: function () {
          this.extension = getExtension(this.fileName) 
      },
      download(){
          if(this.index < this.uploader.dowanloadNumber && this.isWaiting()){
            this.bootstrap();
          }
      },
    },
    mounted () {
      this.size = this.file.size
      this.fileName = this.file.fileName
      this.zipDownload = this.file.zipDownload
      this.saveType = this.uploader.saveType
      this.getFormatSize();
      this.getExtension();
      this.download();
      
    },
    destroyed () {
      this.cancelHandle()
      
    }
  }
</script>

<style>
  .uploader-file {
    position: relative;
    height: 49px;
    line-height: 49px;
    overflow: hidden;
    border-bottom: 1px solid #e8e8e8;
  }
  .uploader-file[status="waiting"] .uploader-file-pause,
  .uploader-file[status="uploading"] .uploader-file-pause {
    display: block;
  }
  .uploader-file[status="paused"] .uploader-file-resume {
    display: block;
  }
  .uploader-file[status="error"] .uploader-file-retry {
    display: block;
  }
  .uploader-file[status="success"] .uploader-file-remove {
    display: none;
  }
  .uploader-file[status="error"] .uploader-file-progress {
    background: #ffe0e0;
  }
  .uploader-file-progress {
    position: absolute;
    width: 100%;
    height: 100%;
    background: #e2eeff;
    transform: translateX(-100%);
  }
  .uploader-file-progressing {
    transition: all .4s linear;
  }
  .uploader-file-info {
    position: relative;
    z-index: 1;
    height: 100%;
    overflow: hidden;
  }
  .uploader-file-info:hover {
    background-color: rgba(240, 240, 240, 0.2);
  }
  .uploader-file-info i,
  .uploader-file-info em {
    font-style: normal;
  }
  .uploader-file-name,
  .uploader-file-size,
  .uploader-file-meta,
  .uploader-file-status,
  .uploader-file-actions {
    float: left;
    position: relative;
    height: 100%;
  }
  .uploader-file-name {
    width: 45%;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    text-indent: 14px;
  }
  .uploader-file-icon {
    width: 24px;
    height: 24px;
    display: inline-block;
    vertical-align: top;
    margin-top: 13px;
    margin-right: 8px;
  }
  .uploader-file-icon::before {
    content: "📃";
    display: block;
    height: 100%;
    font-size: 24px;
    line-height: 1;
    text-indent: 0;
  }
  .uploader-file-icon[icon="folder"]::before {
    content: "📂";
  }
  .uploader-file-icon[icon="image"]::before {
    content: "📊";
  }
  .uploader-file-icon[icon="video"]::before {
    content: "📹";
  }
  .uploader-file-icon[icon="audio"]::before {
    content: "🎵";
  }
  .uploader-file-icon[icon="document"]::before {
    content: "📋";
  }
  .uploader-file-size {
    width: 13%;
    text-indent: 10px;
  }
  .uploader-file-meta {
    width: 8%;
  }
  .uploader-file-status {
    width: 24%;
    text-indent: 20px;
  }
  .uploader-file-actions {
    width: 10%;
  }
  .uploader-file-actions > span {
    display: none;
    float: left;
    width: 16px;
    height: 16px;
    margin-top: 16px;
    margin-right: 10px;
    cursor: pointer;
    background: url("") no-repeat 0 0;
  }
  .uploader-file-actions > span:hover {
    background-position-x: -21px;
  }
  .uploader-file-actions .uploader-file-pause {
    background-position-y: 0;
  }
  .uploader-file-actions .uploader-file-resume {
    background-position-y: -17px;
  }
  .uploader-file-actions .uploader-file-retry {
    background-position-y: -53px;
  }
  .uploader-file-actions .uploader-file-remove {
    display: block;
    background-position-y: -34px;
  }

 .uploader-file[status="success"] .dotting{
   display: inline-block;
 }
.dotting {

display: none; width: 10px; min-height: 2px;
padding-right: 2px;
border-left: 2px solid currentColor; border-right: 2px solid currentColor;
background-color: currentColor; background-clip: content-box;
box-sizing: border-box;
-webkit-animation: dot 4s infinite step-start both;
animation: dot 4s infinite step-start both;

}
.dotting:before { content: '...'; } /* IE8 */
.dotting::before { content: ''; }
:root .dotting { margin-left: 2px; padding-left: 2px; } /* IE9+ */

@-webkit-keyframes dot {
25% { border-color: transparent; background-color: transparent; }
50% { border-right-color: transparent; background-color: transparent; }
75% { border-right-color: transparent; }
}
@keyframes dot {
25% { border-color: transparent; background-color: transparent; }
50% { border-right-color: transparent; background-color: transparent; }
75% { border-right-color: transparent; }
}
</style>
